package com.unito.tableplus.server.services;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
//import java.util.List;
import java.util.logging.Logger;
import java.io.*;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.Drive.Files;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import com.google.api.services.drive.model.Permission;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.User;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.unito.tableplus.client.services.DriveService;
import com.unito.tableplus.server.persistence.UserQueries;
import com.unito.tableplus.server.persistence.WalletQueries;
import com.unito.tableplus.server.util.ServiceFactory;
import com.unito.tableplus.server.util.Utility;
import com.unito.tableplus.shared.model.DriveFile;
import com.unito.tableplus.shared.model.Wallet;

public class DriveServiceImpl extends RemoteServiceServlet implements
		DriveService {

	private static final long serialVersionUID = 2603943737426137505L;
	 private static final Logger log = Logger.getAnonymousLogger();

	// MAGIO per app nostra
	/*
	 * private static final String CLIENT_ID =
	 * "899809005739.apps.googleusercontent.com"; private static final String
	 * CLIENT_SECRET = "GEO0G7BcohMenRnhJix1pfeA"; private static final String
	 * PROVIDER = "?provider=drive"; private static final String REDIRECT_URI =
	 * Utility.getCallbackUrl() + PROVIDER;
	 */
	// END MAGIO

	private static final String CLIENT_ID = "843761346041.apps.googleusercontent.com";
	private static final String CLIENT_SECRET = "GEO0G7BcohMenRnhJix1pfeA";
	private static final String PROVIDER = "?provider=drive";
	private static final String REDIRECT_URI = Utility.getCallbackUrl()
			+ PROVIDER;

	private static GoogleAuthorizationCodeFlow flow;

	private static final UserService userService = ServiceFactory
			.getUserService();

	@Override
	public String getAuthorizationURL() {
		String url = "";
		try {
		HttpTransport httpTransport = new NetHttpTransport();
		JsonFactory jsonFactory = new JacksonFactory();
		 log.info("getAuth 0");
		GoogleAuthorizationCodeFlow.Builder builder = new GoogleAuthorizationCodeFlow.Builder(httpTransport,
				 		jsonFactory, CLIENT_ID, CLIENT_SECRET,
				 		Arrays.asList(DriveScopes.DRIVE));
		 log.info("getAuth 1");
		builder = builder.setAccessType("offline");
		 log.info("getAuth 2");
		 log.info("getAuth 3");
		builder = builder.setApprovalPrompt("force");
		 log.info("getAuth 4");
		flow = builder.build(); 
		 log.info("getAuth 5");
	/*	 flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport,
		 		jsonFactory, CLIENT_ID, CLIENT_SECRET,
		 		Arrays.asList(DriveScopes.DRIVE)).setAccessType("offline")
		 	.setApprovalPrompt("force").build(); */
		 url = flow.newAuthorizationUrl().setRedirectUri(REDIRECT_URI)
				.build(); 
		 log.info("getAuth 6");
		} catch (Exception e) {
			 final Writer result = new StringWriter();
			    final PrintWriter printWriter = new PrintWriter(result);
			    e.printStackTrace(printWriter);
			log.info(result.toString());
		}
		return url;
	}

	public static void storeCredentials(String authorizationCode)
			throws IOException {
		 log.info("storecredential 0");
		User user = userService.getCurrentUser();
		HttpTransport transport = new NetHttpTransport();
		JsonFactory jsonFactory = new JacksonFactory();
		GoogleTokenResponse response = flow.newTokenRequest(authorizationCode)
				.setRedirectUri(REDIRECT_URI).execute();
		 log.info("storecredential 1");
		Credential credential = new GoogleCredential.Builder()
				.setTransport(transport).setJsonFactory(jsonFactory)
				.setClientSecrets(CLIENT_ID, CLIENT_SECRET).build()
				.setFromTokenResponse(response);
		 log.info("storecredential 2");
		 String k = user.getEmail();
		 if (k==null) log.info("key NULL");
		Long userKey = UserQueries.queryUser("email", user.getEmail()).getKey();
		 log.info("storecredential 3");
		if (userKey != null && credential != null) {
			Wallet wallet = WalletQueries.getWallet(userKey);
			wallet.setDriveAccessToken(credential.getAccessToken());
			wallet.setDriveRefreshToken(credential.getRefreshToken());
			WalletQueries.storeWallet(wallet);
		}
		 log.info("storecredential 4");
	}

	public static ArrayList<DriveFile> loadFiles(Wallet wallet) throws IOException {
		//public static List<DriveFile> loadFiles(Wallet wallet) throws IOException {
		//List<DriveFile> driveFiles = new ArrayList<DriveFile>();
		ArrayList<DriveFile> driveFiles = new ArrayList<DriveFile>();
		Drive service = getService(wallet);

		ArrayList<File> result = new ArrayList<File>();
		//List<File> result = new ArrayList<File>();
		Files.List request = service.files().list();
		do {
			FileList files = request.execute();
			result.addAll(files.getItems());
			request.setPageToken(files.getNextPageToken());
		} while (request.getPageToken() != null
				&& request.getPageToken().length() > 0);
		DriveFile df;
		for (File f : result) {
			df = new DriveFile();
			df.setTitle(f.getTitle());
			df.setLink(f.getSelfLink());
			// df.setID(f.getId()); MAGIO
			df.setKey(f.getId());
			df.setURI(f.getAlternateLink());
			// df.setURI(f.getSelfLink()); MAGIO
			//System.out.println("##### selFLink " + f.getSelfLink());
			//System.out.println("##### alternate " + f.getAlternateLink());
			//System.out.println("##### title " + f.getTitle());
			driveFiles.add(df);
		}

		return driveFiles;
	}

	public static Drive getService(Wallet wallet) {
		HttpTransport transport = new NetHttpTransport();
		JsonFactory jsonFactory = new JacksonFactory();

		GoogleCredential credential = new GoogleCredential.Builder()
				.setJsonFactory(jsonFactory).setTransport(transport)
				.setClientSecrets(CLIENT_ID, CLIENT_SECRET).build();
		credential.setAccessToken(wallet.getDriveAccessToken());
		credential.setRefreshToken(wallet.getDriveRefreshToken());
		return new Drive.Builder(transport, jsonFactory, credential).build();
	}

	/**
	 * https://developers.google.com/drive/v2/reference/permissions/insert
	 * Insert a new permission.
	 * 
	 * @param service
	 *            Drive API service instance.
	 * @param fileId
	 *            ID of the file to insert permission for.
	 * @param value
	 *            User or group e-mail address, domain name or {@code null}
	 *            "default" type.
	 * @param type
	 *            The value "user", "group", "domain" or "default".
	 * @param role
	 *            The value "owner", "writer" or "reader".
	 * @return The inserted permission if successful, {@code null} otherwise.
	 */
	private static Permission insertPermission(Drive service, String fileId,
			String value, String type, String role) {
		Permission newPermission = new Permission();

		newPermission.setValue(value);
		newPermission.setType(type);
		newPermission.setRole(role);
		try {
			return service.permissions().insert(fileId, newPermission)
					.execute();
		} catch (IOException e) {
			System.err.println("An error occurred: " + e);
		}
		return null;
	}

	/**
	 * https://developers.google.com/drive/v2/reference/permissions/update
	 * Update a permission's role.
	 * 
	 * @param service
	 *            Drive API service instance.
	 * @param fileId
	 *            ID of the file to update permission for.
	 * @param permissionId
	 *            ID of the permission to update.
	 * @param newRole
	 *            The value "owner", "writer" or "reader".
	 * @return The updated permission if successful, {@code null} otherwise.
	 */
	@SuppressWarnings("unused")
	private static Permission updatePermission(Drive service, String fileId,
			String permissionId, String newRole) {
		try {
			// First retrieve the permission from the API.
			Permission permission = service.permissions()
					.get(fileId, permissionId).execute();
			permission.setRole(newRole);
			return service.permissions()
					.update(fileId, permissionId, permission).execute();
		} catch (IOException e) {
			System.out.println("An error occurred: " + e);
		}
		return null;
	}

	/**
	 * Share a file with a list of users.
	 * 
	 * @param file
	 *            The id of the file to share
	 * @param wallet
	 *            File owner's wallet
	 * @param users
	 *            Users' email addresses list to share the file with
	 */
	public static void shareFile(String fileId, Wallet wallet, ArrayList<String> users) {
		//public static void shareFile(String fileId, Wallet wallet, List<String> users) {
		Drive service = getService(wallet);
		for (String u : users)
			insertPermission(service, fileId, u, "user", "owner");
	}

	/**
	 * Share a file with a user.
	 * 
	 * @param fileID
	 *            The id of the file to share
	 * @param wallet
	 *            File owner's wallet
	 * @param user
	 *            User's email address to share the file with
	 */
	public static void shareFile(String fileID, Wallet wallet, String user) {
		Drive service = getService(wallet);
		insertPermission(service, fileID, user, "user", "owner");
	}

	/**
	 * Share a list of file with a user.
	 * 
	 * @param fileIDs
	 *            The ids of the files to share
	 * @param wallet
	 *            File owner's wallet
	 * @param user
	 *            User's email address to share the file with
	 */
	public static void shareFiles(ArrayList<String> fileIDs, Wallet wallet, String user) {
		//public static void shareFiles(List<String> fileIDs, Wallet wallet, String user) {
		Drive service = getService(wallet);
		for (String fileID : fileIDs)
			insertPermission(service, fileID, user, "user", "owner");
	}

}
